﻿//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.Helpers;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System.Linq;
using Tripper.Tools.Math;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    public class Temple_of_the_Jade_Serpent_Heroic : Dungeon
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 468; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(957.2355, -2476.059, 180.9084); }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret as WoWUnit;
                    if (unit != null)
                    {
                        if (unit.Entry == WiseMariId && unit.HasAura("Water Bubble") && !Me.IsHealer())
                            return true;

                        // prevent bot from trying to kill these while doing Wise Mari encounter.
                        if ((unit.Entry == LesserSha || unit.Entry == MinionOfDoubt) && unit.Combat && Me.Combat && !unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember)
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null) { }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if ((unit.Entry == StrifeId || unit.Entry == PerilId) && unit.HasAura("Intensity") && unit.Auras["Intensity"].StackCount >= 9)
                        priority.Score -= 500;

                    if (unit.Entry == HauntingShaId)
                        priority.Score += 200;

                    if (unit.Entry == FragmentOfDoubt)
                        priority.Score += 200;
                }
            }
        }

        #endregion

        private const uint ShaResidueId = 106653;
        private const uint WiseMariId = 56448;
        private const uint WiseMariFireHoseTargetId = 56574;
        private const uint StrifeId = 59051;
        private const uint PerilId = 59726;
        private const uint HauntingShaId = 59555;
        private const int ShadowsOfDoubt = 110099;
        private const uint DragonWave = 56789;
        private const uint JadeFire = 56893;
        private const uint FragmentOfDoubt = 56792;
        private const uint LesserSha = 58319;
        private const uint MinionOfDoubt = 57109;

        private static readonly WoWPoint[] WiseMariSafeSpots = new[]
        { // inner area
            new WoWPoint(1051.405, -2553.585, 174.6977), new WoWPoint(1052.822, -2554.836, 174.6978),
            new WoWPoint(1054.469, -2557.322, 174.6978), new WoWPoint(1055.248, -2560.337, 174.6978),
            new WoWPoint(1054.762, -2562.604, 174.6978), new WoWPoint(1053.547, -2565.319, 174.6978),
            new WoWPoint(1048.494, -2568.502, 174.6978), new WoWPoint(1045.432, -2568.629, 174.6977),
            new WoWPoint(1042.862, -2567.668, 174.6977), new WoWPoint(1040.639, -2565.951, 174.6978),
            new WoWPoint(1038.646, -2560.687, 174.6978), new WoWPoint(1039.012, -2557.942, 174.6978),
            new WoWPoint(1040.285, -2555.325, 174.6978), new WoWPoint(1042.682, -2553.407, 174.6978),
            new WoWPoint(1045.162, -2552.474, 174.6978), new WoWPoint(1047.432, -2552.278, 174.6977),
            // outer area
            new WoWPoint(1047.545, -2546.342, 174.6978), new WoWPoint(1043.129, -2546.999, 174.6978),
            new WoWPoint(1039.361, -2548.945, 174.6978), new WoWPoint(1035.902, -2551.642, 174.6978),
            new WoWPoint(1033.8, -2555.948, 174.6978), new WoWPoint(1033.109, -2560.566, 174.6978),
            new WoWPoint(1029.522, -2554.528, 174.6978), new WoWPoint(1026.834, -2572.702, 174.6978),
            new WoWPoint(1029.761, -2576.143, 174.6978), new WoWPoint(1031.975, -2579.165, 174.6978),
            new WoWPoint(1035.268, -2579.458, 174.6978), new WoWPoint(1039.015, -2575.475, 174.6978),
            new WoWPoint(1036.832, -2569.53, 174.6978), new WoWPoint(1040.178, -2572.773, 174.6978),
            new WoWPoint(1044.089, -2573.994, 174.6977), new WoWPoint(1048.111, -2573.905, 174.6978),
            new WoWPoint(1050.307, -2573.528, 174.6978), new WoWPoint(1045.066, -2577.509, 174.6978),
            new WoWPoint(1040.632, -2579.016, 174.6977), new WoWPoint(1042.359, -2576.134, 174.6977),
            new WoWPoint(1053.788, -2582.024, 174.6978), new WoWPoint(1048.276, -2583.024, 174.6978),
            new WoWPoint(1046.994, -2589.204, 174.6979), new WoWPoint(1043.431, -2589.852, 174.6978),
            new WoWPoint(1040.591, -2593.741, 174.703), new WoWPoint(1040.438, -2585.912, 174.6977),
            new WoWPoint(1044.145, -2585.725, 174.6978), new WoWPoint(1033.802, -2584.221, 174.6978),
            new WoWPoint(1034.886, -2589.313, 174.6982), new WoWPoint(1033.678, -2592.558, 174.702),
            new WoWPoint(1038.613, -2592.392, 174.701), new WoWPoint(1057.845, -2569.239, 174.6978),
            new WoWPoint(1059.668, -2564.843, 174.6978), new WoWPoint(1061.083, -2560.323, 174.6978),
            new WoWPoint(1059.867, -2555.553, 174.6978), new WoWPoint(1057.377, -2551.288, 174.6978),
            new WoWPoint(1054.945, -2548.756, 174.6978), new WoWPoint(1064.203, -2566.805, 174.6984),
            new WoWPoint(1066.948, -2565.171, 174.6978)
        };


        private readonly WoWPoint[] _wiseMariCorruptLivingTankSpots = new[] { new WoWPoint(1049.255, -2585.932, 174.6978), new WoWPoint(1032.726, -2583.954, 174.6978) };
        private WoWUnit[] _dragonWaves;
        private WoWUnit _liuFlameheart;

        private WoWUnit _wiseMari;

        private readonly CircularQueue<WoWPoint> _wiseMariCirclePath = new CircularQueue<WoWPoint>
        {
            new WoWPoint(1051.295, -2553.112, 174.6977),
            new WoWPoint(1054.035, -2556.29, 174.6978),
            new WoWPoint(1055.2, -2562.44, 174.6978),
            new WoWPoint(1053.14, -2566.464, 174.6978),
            new WoWPoint(1048.866, -2568.538, 174.6978),
            new WoWPoint(1046.027, -2569.12, 174.6977),
            new WoWPoint(1042.23, -2567.977, 174.6977),
            new WoWPoint(1040.654, -2566.208, 174.6978),
            new WoWPoint(1038.396, -2560.746, 174.6978),
            new WoWPoint(1039.406, -2556.696, 174.6978),
            new WoWPoint(1042.437, -2553.605, 174.6978),
            new WoWPoint(1047.244, -2551.961, 174.6977),
        };

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(0, "Root")]
        public Composite RootHandler()
        {
            return new PrioritySelector(
                // Corrupt Living Water npc drop this after they die.
                ScriptHelpers.CreateRunAwayFromBad(ctx => !Me.IsMoving, 6, ShaResidueId),
                // dropped by minion of doubt
                ScriptHelpers.CreateRunAwayFromBad(ctx => !Me.IsMoving,6,ShadowsOfDoubt));
        }

        [EncounterHandler(56448, "Wise Mari")]
        public Composite WiseMariEncounter()
        {
            List<WoWPoint> shaResidueLocs = null;
            WoWPoint safeLoc = WoWPoint.Zero;
            float relativeSpoutAngle = 0;
            bool cycledToNearestCircularPoint = false;

            return new PrioritySelector(
                ctx => _wiseMari = ctx as WoWUnit,
                // Phase 1
                new Decorator<WoWUnit>(
                    boss => boss.HasAura("Water Bubble"),
                    new PrioritySelector(
                        ctx =>
                        {
                            if (cycledToNearestCircularPoint)
                                cycledToNearestCircularPoint = false;
                            shaResidueLocs = ObjectManager.GetObjectsOfType<WoWDynamicObject>().Where(u => u.Entry == ShaResidueId).Select(u => u.Location).ToList();
                            return ctx;
                        },
                        ScriptHelpers.CreateRunAwayFromBad(ctx => true, 7f, u => u.Entry == WiseMariId && ((WoWUnit)u).HasAura("Water Bubble")),
                // tank the corrupt Living waters in the back
                        new Decorator(
                            ctx => Me.IsTank() && (Targeting.Instance.IsEmpty() || Targeting.Instance.TargetList.All(u => u.CurrentTargetGuid == Me.Guid)),
                            new PrioritySelector(
                                ctx =>
                                safeLoc =
                                _wiseMariCorruptLivingTankSpots.OrderBy(l => l.DistanceSqr(Me.Location)).FirstOrDefault(l => !shaResidueLocs.Any(s => s.DistanceSqr(l) < 6 * 6)),
                                new Decorator(ctx => StyxWoW.Me.Location.DistanceSqr(safeLoc) > 4 * 4, new Action(ctx => Navigator.MoveTo(safeLoc))))),
                // don't stand in the pools.
                        new Decorator(
                            ctx =>
                            Me.HasAura("Corrupted Waters") &&
                            (!Me.IsTank() || (Me.IsTank() && (Targeting.Instance.IsEmpty() || Targeting.Instance.TargetList.All(u => u.CurrentTargetGuid == Me.Guid)))),
                            new PrioritySelector(
                                ctx =>
                                safeLoc =
                                WiseMariSafeSpots.OrderBy(l => l.DistanceSqr(Me.IsMelee() && Me.CurrentTargetGuid > 0 ? Me.CurrentTarget.Location : Me.Location)).
                                    FirstOrDefault(l => !shaResidueLocs.Any(s => s.DistanceSqr(l) < 6 * 6)),
                                new Decorator(ctx => StyxWoW.Me.Location.Distance(safeLoc) > Navigator.PathPrecision, new Action(ctx => Navigator.MoveTo(safeLoc))),
                                new Decorator(
                                    ctx => StyxWoW.Me.Location.Distance(safeLoc) <= Navigator.PathPrecision, new Action(ctx => Navigator.PlayerMover.MoveTowards(safeLoc))))))),
                // Phase 2
                new Decorator<WoWUnit>(
                    boss => !boss.HasAura("Water Bubble"),
                    new PrioritySelector(
                        ctx =>
                        {
                            if (!cycledToNearestCircularPoint)
                            {
                                var myLoc = Me.Location;
                                var nearestLoc = _wiseMariCirclePath.OrderBy(l => l.DistanceSqr(myLoc)).FirstOrDefault();
                                _wiseMariCirclePath.CycleTo(nearestLoc);
                                cycledToNearestCircularPoint = true;
                            }
                            relativeSpoutAngle = GetRelativeSpoutAngle(_wiseMari);
                            return ctx;
                        },
                // move closer
                        new Decorator(
                            ctx => _wiseMari.DistanceSqr > 13 * 13,
                            new Action(ctx => Navigator.MoveTo(WoWMathHelper.CalculatePointFrom(Me.Location, _wiseMari.Location, 8)))),
                        new Decorator(ctx => (relativeSpoutAngle > 0 && relativeSpoutAngle < 90 || Me.Z < 174.5) && Me.Z > 174,
                            new Sequence(
                                new Action(ctx =>
                                               {
                                                   var point = _wiseMariCirclePath.Peek();
                                                   Navigator.PlayerMover.MoveTowards(point);
                                                   if (Me.Location.Distance(point) < 3)
                                                       _wiseMariCirclePath.Dequeue();
                                                   var ang = GetRelativeSpoutAngle(_wiseMari);
                                                   // keep moving if standing in water.
                                                   if ((ang < 200 || Me.Z < 174.5) && _wiseMari.IsAlive)
                                                       return RunStatus.Running;
                                                   WoWMovement.MoveStop();
                                                   return RunStatus.Success;
                                               }),
                                               new WaitContinue(3, ctx => Me.IsMoving, new ActionAlwaysSucceed()),
                                               new DecoratorContinue(ctx => !Me.IsMoving,
                                                   new Action(ctx => _wiseMari.Face())))
                            ))),
                // Tank does nothing if there are not targets in target list.
                new Decorator(ctx => StyxWoW.Me.IsTank() && Targeting.Instance.IsEmpty(), new ActionAlwaysSucceed()));
        }

        /*
        private void CircleStrifeBehindUnit(WoWUnit unit, float dist)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                var unitLoc = unit.Location;
                var myLoc = Me.Location;
                var myRot = Me.RotationDegrees;
                var unitRot = unit.RotationDegrees;

                var rotation = myRot >= unitRot ? myRot - unitRot : 360 - (unitRot - myRot);

                if (!WoWMovement.IsFacing)
                    WoWMovement.ConstantFace(unit.Guid);

                if (unitLoc.Distance(myLoc) > dist + 1)
                {
                    if (!StyxWoW.Me.MovementInfo.MovingForward)
                        Lua.DoString("MoveForwardStart()");
                }
                else if (StyxWoW.Me.MovementInfo.MovingForward)
                    Lua.DoString("MoveForwardStop()");

                if (unitLoc.Distance(myLoc) < dist - 1)
                {
                    if (!StyxWoW.Me.MovementInfo.MovingBackward)
                        Lua.DoString("MoveBackwardStart()");
                }
                else if (StyxWoW.Me.MovementInfo.MovingBackward)
                    Lua.DoString("MoveBackwardStop()");

                if (rotation < 150)
                {
                    if (!StyxWoW.Me.MovementInfo.MovingStrafeLeft)
                        Lua.DoString("StrafeLeftStart()");
                }
                else if (StyxWoW.Me.MovementInfo.MovingStrafeLeft && rotation > 210 && myLoc.Z > 174.5f)
                    Lua.DoString("StrafeLeftStop()");
            }
        }
        */

        private float GetRelativeSpoutAngle(WoWUnit wiseMari)
        {
            var firehoseTarget = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == WiseMariFireHoseTargetId);
            var ang = 0f;
            if (firehoseTarget != null)
            {
                var mariToTarget = firehoseTarget.Location - wiseMari.Location;
                var mariAng = (float)Math.Atan2(mariToTarget.Y, mariToTarget.X);
                var mariToMe = Me.Location - wiseMari.Location;
                var angToMe = (float)Math.Atan2(mariToMe.Y, mariToMe.X);
                ang = WoWMathHelper.RadiansToDegrees(mariAng - angToMe);
                if (ang < 0)
                    ang = 360 + ang;
            }
            return ang;
        }

        [EncounterHandler(56843, "Lorewalker Stonestep", Mode = CallBehaviorMode.Proximity)]
        public Composite LorewalkerStonestepEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(ctx => boss.HasAura("Meditation") && ScriptHelpers.IsBossAlive("Lorewalker Stonestep"),
                    new Action(ctx => ScriptHelpers.MarkBossAsDead("Lorewalker Stonestep"))
                ));
        }

        //

        [EncounterHandler(56732, "Liu Flameheart", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite LiuFlameheartPreEncounter()
        {
            var roomCenterLoc = new WoWPoint(929.55, -2560.471, 180.0693);
            return new PrioritySelector(
                ctx => _liuFlameheart = ctx as WoWUnit, new Decorator(ctx => _liuFlameheart == null, ScriptHelpers.CreateClearArea(() => roomCenterLoc, 70, null)));
        }


        [EncounterHandler(56732, "Liu Flameheart")]
        public Composite LiuFlameheartEncounter()
        {
            return new PrioritySelector(
                ctx =>
                {
                    _dragonWaves = ObjectManager.GetObjectsOfType<WoWUnit>().Where(u => u.Entry == DragonWave).OrderBy(u => u.DistanceSqr).ToArray();
                    return _liuFlameheart = ctx as WoWUnit;
                },
                ScriptHelpers.CreateRunAwayFromLocation(ctx => DragonWaveLoc(_dragonWaves, 0) != WoWPoint.Zero, 6, () => DragonWaveLoc(_dragonWaves, 0)),
                ScriptHelpers.CreateRunAwayFromLocation(ctx => DragonWaveLoc(_dragonWaves, 1) != WoWPoint.Zero, 6, () => DragonWaveLoc(_dragonWaves, 1)),
                ScriptHelpers.CreateRunAwayFromLocation(ctx => DragonWaveLoc(_dragonWaves, 2) != WoWPoint.Zero, 6, () => DragonWaveLoc(_dragonWaves, 2)),
                ScriptHelpers.CreateRunAwayFromLocation(ctx => DragonWaveLoc(_dragonWaves, 3) != WoWPoint.Zero, 6, () => DragonWaveLoc(_dragonWaves, 3)),
                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 5, JadeFire, 3152));
        }

        [EncounterHandler(56439, "Sha of Doubt")]
        public Composite ShaOfDoubtEncounter()
        {
            return new PrioritySelector(
                ScriptHelpers.CreateDispellParty("Touch of Nothingness", ScriptHelpers.PartyDispellType.Magic),
                // run away from other party members if I have touch of nothingness.
                ScriptHelpers.CreateRunAwayFromBad(ctx => StyxWoW.Me.HasAura("Touch of Nothingness"), 8, u => u is WoWPlayer && !u.IsMe),
                // kite 
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => StyxWoW.Me.HasAura("Touch of Nothingness"),
                    () => ScriptHelpers.Healer.Location,
                    35,
                    15,
                    u => u.Entry == FragmentOfDoubt && ((WoWUnit)u).CurrentTargetGuid == StyxWoW.Me.Guid),
                new Decorator(
                    ctx =>
                    {
                        var tank = ScriptHelpers.Tank;
                        return !StyxWoW.Me.IsTank() && tank != null && tank.DistanceSqr > 4 * 4 && !StyxWoW.Me.HasAura("Touch of Nothingness");
                    },
                    new Action(ctx => Navigator.MoveTo(ScriptHelpers.Tank.Location))));
        }

        private WoWPoint DragonWaveLoc(WoWUnit[] dragonWaves, int index)
        {
            if (dragonWaves == null || dragonWaves.Length < index + 1)
                return WoWPoint.Zero;
            var loc = StyxWoW.Me.Location.GetNearestPointOnSegment(dragonWaves[index].Location, _liuFlameheart.Location.RayCast(dragonWaves[index].Rotation, 80));
            return loc;
        }
    }
}